using System;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT;
using GHIElectronics.NETMF.Hardware;
using System.Runtime.CompilerServices;


namespace Microsoft.NETMF.BikeComputer.Hardware
{
    public class PressureSensor
    {
        const int c_SHIFT = 2;
        const int c_Direction__READ = 0x00;
        const int c_Direction__WRITE = 0x02;
        const byte c_Register__DATAWR = 0x01 << c_SHIFT;
        const byte c_Register__ADDPTR = 0x02 << c_SHIFT;
        const byte c_Register__OPERATION = 0x03 << c_SHIFT;
        const byte c_Register__RESET = 0x06 << c_SHIFT;
        const byte c_Register__STATUS = 0x07 << c_SHIFT;
        const byte c_Register__DATARD8 = 0x1f << c_SHIFT;
        const byte c_Register__DATARD16 = 0x20 << c_SHIFT;
        const byte c_Register__TEMPOUT = 0x21 << c_SHIFT;
        const byte c_Register__REVID = 0x00 << c_SHIFT;
        //--//

        public PressureSensor()
        {
            byte[] writeBuffer = new byte[] { 0x00, 0x00 };
            byte[] readBuffer = new byte[] { 0x00, 0x00 };


            //  initialization of the hardware while the runtime is not up yet is unreliable so we will power the sensor down and restart it in a controlled way.
            _PD = new OutputPort(GHIElectronics.NETMF.Hardware.EMX.Pin.IO12, true);

            // set up the SPI port 
            SPI.Configuration pressureSensorConfiguration = new SPI.Configuration(
             GHIElectronics.NETMF.Hardware.EMX.Pin.IO11,        // use GPIO pin IO11 for the chip select (in case there are multiple SPI devices on this bus
             false,                                             // set the pin low to accext the sensor
             0,                                                 // setup time set to 0
             0,                                                 // set delay after transfer to 0
             false,                                             // clock line is low when chip is not selected/communicating
             true,                                              // data is sampled on the rising edge of the clock cycle
             500,                                               // 500 is the max clock rate that this sensor supports
             SPI.SPI_module.SPI2                                // the device will be connected to SPI port #2
             );

           pressureSensor = new SPI( pressureSensorConfiguration);

            // Set up the DRDY InterruptPort on IO26
            _DRDY = new InterruptPort(GHIElectronics.NETMF.Hardware.EMX.Pin.IO26, true, Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeHigh);
            
            //power back up to start the setup process
            PressureSensorPowerUp();

            // delay 60 ms
            System.Threading.Thread.Sleep(60);

            // loop testing for successful completion of startup
            byte status = 0xFF;
            for (int i = 0; i < 10; i++)
            {
                status = CheckStatus();
                if(verbose) Debug.Print("Startup -- STATUS: " + status.ToString());
                if ((status & 0x01) == 0) break;
                System.Threading.Thread.Sleep(10);
            }
            if ((status & 0x01)  != 0)
            {
               throw new Exception( " Error in completing startup " );
            }

            // check that startup is completed
            writeBuffer[0] = c_Register__DATARD8 | c_Direction__READ;
            pressureSensor.WriteRead(writeBuffer, readBuffer,1);
            if ((readBuffer[0] & 0x01) != 1)
            {
               throw new Exception( " Error in Checksum after startup " );
            }

            // Select the low power triggered mode 

            writeBuffer[0] = c_Register__ADDPTR | c_Direction__WRITE;
            writeBuffer[1] = 0x00;
            pressureSensor.Write(writeBuffer);
            writeBuffer[0] = c_Register__DATAWR | c_Direction__WRITE;
            writeBuffer[1] = 0x05;
            pressureSensor.Write(writeBuffer);
            writeBuffer[0] = c_Register__OPERATION | c_Direction__WRITE;
            writeBuffer[1] = 0x02;
            pressureSensor.Write(writeBuffer);
            System.Threading.Thread.Sleep(50);

            _DRDY.OnInterrupt += new NativeEventHandler(drdy_OnInterrupt);

        }

        private byte CheckStatus()
        {
            byte[] writeBuffer = new byte[] { 0x00 };
            byte[] readBuffer = new byte[] {0x00};

            writeBuffer[0] = c_Register__STATUS | c_Direction__READ;
            pressureSensor.WriteRead (writeBuffer,readBuffer,1);
            return readBuffer[0];
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void TriggerPressure()
        {
            if (!sensorPaused)
            {
               // write 0x0c to 0x03 (the Operation register)  This will trigger the pressure collection
                byte[] writeBuffer = new byte[] { 0x00, 0x00 };

                writeBuffer[0] = c_Register__OPERATION | c_Direction__WRITE;
                writeBuffer[1] = 0x0c;
                pressureSensor.Write(writeBuffer);
            }
        }


        public double GetHeight()
        {
            double returnHeight = heightFt;
            heightFt = 0;
            return returnHeight;
        }

        public double GetTemperature()
        {
            double returnTemp = temperature;
            temperature = 0;
            return returnTemp;
        }
           
        [MethodImpl(MethodImplOptions.Synchronized)]
        private void drdy_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            //DRDY event handler
            // read TEMPOUT register (0x21) - 13 bits low bits
            // read DATARD8 register (0x1f) - bits [2:0]  MSB pressure data
            // read DATARD16 register (0x20) - 16 LSB of pressure data
            // convert temp
            //convert pressure

            byte[] register = new byte[] {0};
            byte[] tempBytes = new byte[2];
            byte[] pressureByteMSB = new byte[1];
            byte[] pressureByteLSB = new byte[2];

            int pressure = 0;
            byte status;

            status = CheckStatus();
            if (verbose) Debug.Print("Status on Interupt (before read): " + status.ToString()); 

            register[0] = c_Register__TEMPOUT | c_Direction__READ; //  temp register address
            pressureSensor.WriteRead(register, tempBytes,1);  

            register[0] = c_Register__DATARD8 | c_Direction__READ;   // DATARD8 register address
            pressureSensor.WriteRead(register, pressureByteMSB, 1);// just returning one byte - in which byte?
            
            register[0] = c_Register__DATARD16 | c_Direction__READ;  //DATARD16 register Addresss
            pressureSensor.WriteRead(register, pressureByteLSB, 1);

            // compute temperature
            // temperature is 2's compliment
            if (((int) tempBytes[0] & (int) 0x20) > 0)
            {
                temperature = (0x1F & ~tempBytes[0]) << 8;
                temperature += ~tempBytes[1];
                temperature++;
                temperature *= -1;
            }
            else
            {
                temperature = (0x1F & tempBytes[0]) << 8;
                temperature += tempBytes[1];
            }
            temperature /= 20;

            // compute pressure
            pressure = (0x07 & pressureByteMSB[0]) << 16;
            pressure +=        pressureByteLSB[0]  << 8;
            pressure +=        pressureByteLSB[1]  << 0;
            pressure >>= 2;


            //compute height in feet
            // source of algorithm: http://www.wrh.noaa.gov/slc/projects/wxcalc/formulas/pressureAltitude.pdf
            
            heightFt = pressure / 101325.0;
            heightFt = System.Math.Pow(heightFt, 0.190284);
            heightFt = (1 - heightFt) * 145366.45;
            if (verbose) Debug.Print(" Height in feet = : " + heightFt.ToString());

            status = CheckStatus();
            if (verbose) Debug.Print("Status on Interrupt (after read): " + status.ToString());
            Debug.Print("Pressure   : " + pressure.ToString());
            if (verbose) Debug.Print("Temperature: " + temperature.ToString());

        }

        private void PressureSensorPowerDown()
        {
            _PD.Write(true);
        }

        private void PressureSensorPowerUp()
        {
            _PD.Write(false);
        }

        public void PauseSensors()
        {
            sensorPaused = true;
        }

        public void StartSensors()
        {
            sensorPaused = false;
        }




        // Private Data
        // 
        private bool verbose = true;
        private bool sensorPaused = false;
        private InterruptPort _DRDY;
        private OutputPort _PD;
        private SPI pressureSensor;
        private double heightFt = 0;
        private double temperature = 0;

    }


}
